﻿using System;
using DotNetCommon.Extensions;
using System.Linq;
using DBUtil.Builders;
using System.Linq.Expressions;
using System.Collections.Generic;

namespace DBUtil.Provider.MySql;

/// <summary>
/// MySql操作对象
/// </summary>
public partial class MySqlAccess : DBAccess
{
    #region InjectOtherHooks
    private void InjectOtherHooks()
    {
        //前缀匹配
        InjectHook("prefix:[member]:string:Length", memberHook1);
        InjectHook("prefix:[member]:System.Text.Json.", memberHook2);
        InjectHook("prefix:[member]:", memberHookAll);
        //other
        //getArrayLength
        InjectHook("other:getArrayLength", getArrayLength);
        //getArrayIndex
        InjectHook("other:getArrayIndex", getArrayIndex);
    }
    private object memberHook1(object arg)
    {
        //[member]:string:Length
        var ctx = arg as MemberHookContext;
        return ctx.db.StringSqlSegment.GetLength(ctx.WrapBracket(ctx.ParameterString));
    }
    private object memberHook2(object arg)
    {
        //[member]:System.Text.Json.
        var ctx = arg as MemberHookContext;
        var typeFullName = ctx.TypeFullName;
        if (typeFullName == "System.Text.Json.Nodes.JsonArray"
            || typeFullName == "System.Text.Json.Nodes.JsonObject")
        {
            //为什么直接返回 json_length? 因为 JsonArray/JsonObject 下面公开的只有一个 Count 属性
            return $"json_length({ctx.ParameterString})";
        }
        throw new Exception($"不支持解析的表达式类型: {ctx.Expression}!");
    }
    private object memberHookAll(object arg)
    {
        //[member]:
        var ctx = arg as MemberHookContext;
        var member = ctx.Expression as MemberExpression;
        var typeFullName = ctx.TypeFullName;
        var tmp = ctx.ParameterString;
        if (ctx.IsJsonParameter(member.Expression)
            || tmp?.StartsWith("json_value(") == true
            || tmp?.StartsWith("json_extract(") == true
            || tmp?.StartsWith("json_unquote") == true)
        {
            //json
            var jsonPathWrap = ctx.db.GetJsonPathSegByPropertyName(member.Member.Name);
            return memberAccess(tmp, jsonPathWrap, true, member.Type);
        }
        return ctx.GetDefault();
    }
    private object getArrayLength(object arg)
    {
        var arr = arg as object[];
        var str = arr[0].ToString();
        var type = arr[1] as Type;
        //string.length byte[].Length
        if (type == typeof(string) || type == (typeof(byte[]))) return $"length({str})";
        return $"json_length({str})";
    }
    private object getArrayIndex(object arg)
    {
        var arr = arg as object[];
        var op1 = arr[0] as string;
        var op2 = arr[1] as string;
        var isConstant = (bool)arr[2];
        var type = arr[3] as Type;
        string member;
        if (isConstant)
        {
            member = $"'$[{op2}]'";
        }
        else
        {
            member = $"concat('$[',{op2},']')";
        }
        return memberAccess(op1, member, isConstant, type);
    }
    #endregion

    private void InitHooks()
    {
        InjectOtherHooks();
        //hook1
        InjectHook("bool System.Collections.Generic.List<T>.Contains(T item)", hook1);
        InjectHook("bool System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource> source, TSource value)", hook1);
        //hook2
        InjectHook("bool System.Text.Json.Nodes.JsonArray.Contains(System.Text.Json.Nodes.JsonNode item)", hook2);
        //hook3
        InjectHook("bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAll<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values, System.Collections.Generic.IEqualityComparer<T> equalityComparer)", hook3);
        InjectHook("bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAll<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values)", hook3);
        InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAll<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects, System.Collections.Generic.IEqualityComparer<T> equalityComparer)", hook3);
        InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAll<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects)", hook3);
        InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObject<T>(System.Text.Json.Nodes.JsonArray jsonArray, T value, System.Collections.Generic.IEqualityComparer<T> equalityComparer)", hook3);
        InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObject<T>(System.Text.Json.Nodes.JsonArray jsonArray, T value)", hook3);
        InjectHook("bool System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource> source, TSource value, System.Collections.Generic.IEqualityComparer<TSource> comparer)", hook3);
        //hook4
        InjectHook("TValue System.Collections.Generic.Dictionary<TKey, TValue>.get_Item(TKey key)", hook4);
        //hook5
        InjectHook("T System.Collections.Generic.List<T>.get_Item(int index)", hook5);
        InjectHook("T System.Collections.Generic.IList<T>.get_Item(int index)", hook5);
        //hook6
        InjectHook("System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(string propertyName)", hook6);
        //hook7
        InjectHook("System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(int index)", hook7);
        //hook8
        InjectHook("string System.Object.ToString()", hook8);
        //hook9
        InjectHook("T System.Text.Json.Nodes.JsonNode.GetValue<T>()", hook9);
        //hook10
        InjectHook("bool System.Text.Json.Nodes.JsonObject.ContainsKey(string propertyName)", hook10);
        //hook11
        InjectHook("bool System.Collections.Generic.Dictionary<TKey, TValue>.ContainsKey(TKey key)", hook11);
        //hook12
        InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAny<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects, System.Collections.Generic.IEqualityComparer<T> equalityComparer)", hook12);
        InjectHook("bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAny<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects)", hook12);
        InjectHook("bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAny<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values, System.Collections.Generic.IEqualityComparer<T> equalityComparer)", hook12);
        InjectHook("bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAny<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values)", hook12);
        //hook13
        InjectHook("T DotNetCommon.Extensions.StringExtensions.ToObject<T>(string input)", hook13);
        InjectHook("System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.ObjectExtensions.ToJsonObject(System.Object obj)", hook13);
        InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.ObjectExtensions.ToJsonArray(System.Object obj)", hook13);
        //hook14
        InjectHook("int System.Linq.Enumerable.Count<TSource>(System.Collections.Generic.IEnumerable<TSource> source)", hook14);
        //hook15
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddFluent<T>(System.Collections.Generic.List<T> list, T item)", hook15);
        InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddFluent<T>(System.Collections.Generic.IList<T> list, T item)", hook15);
        InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.AddFluent(System.Text.Json.Nodes.JsonArray jsonArray, System.Text.Json.Nodes.JsonNode item)", hook15);
        //hook16
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items)", hook16);
        InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.AddRangeFluent<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> items)", hook16);
        //hook17
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveAtFluent<T>(System.Collections.Generic.List<T> list, int index)", hook17);
        InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.RemoveAtFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index)", hook17);
        //hook18
        InjectHook("System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.RemoveFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key)", hook18);
        InjectHook("System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.RemoveFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key)", hook18);
        //hook19
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.ClearFluent<T>(System.Collections.Generic.List<T> list)", hook19);
        InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.ClearFluent(System.Text.Json.Nodes.JsonArray jsonArray)", hook19);
        //hook20
        InjectHook("System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.ClearFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic)", hook20);
        InjectHook("System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.ClearFluent<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> dic)", hook20);
        InjectHook("System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.ClearFluent(System.Text.Json.Nodes.JsonObject jsonObject)", hook20);
        //hook21
        InjectHook("System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key, System.Text.Json.Nodes.JsonNode value)", hook21);
        InjectHook("System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key, TValue value)", hook21);
        InjectHook("System.Collections.Generic.IDictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> dic, TKey key, TValue value)", hook21);
        //hook22
        InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Text.Json.Nodes.JsonNode b)", hook22);
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.List<T> list, int index, T b)", hook22);
        InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.IList<T> list, int index, T b)", hook22);
        InjectHook("T[] DotNetCommon.Extensions.ArrayExtensions.SetFluent<T>(T[] arr, int index, T b)", hook22);
        //hook23
        InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.InsertAtFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Text.Json.Nodes.JsonNode item)", hook23);
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.InsertAtFluent<T>(System.Collections.Generic.List<T> list, int index, T b)", hook23);
        //hook24
        InjectHook("bool System.Linq.Enumerable.Any<TSource>(System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate)", hook24);
        InjectHook("bool System.Linq.Enumerable.All<TSource>(System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate)", hook24);
        InjectHook("bool System.Linq.Enumerable.Any<TSource>(System.Collections.Generic.IEnumerable<TSource> source)", hook24);
        //hook25
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddIfNotExistFluent<T>(System.Collections.Generic.List<T> list, T item)", hook25);
        InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, T item)", hook25);
        //hook26
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items, bool isDistinct)", hook26);
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items)", hook26);
        InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, System.Collections.Generic.IEnumerable<T> items, bool isDistinct)", hook26);
        InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, System.Collections.Generic.IEnumerable<T> items)", hook26);
        //hook27
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveFluent<T>(System.Collections.Generic.List<T> list, T b)", hook27);
        //hook28
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveFluent<T>(System.Collections.Generic.List<T> list, System.Predicate<T> match)", hook28);
        //hook29
        InjectHook("int System.Collections.Generic.List<T>.IndexOf(T item)", hook29);
        //hook30
        InjectHook("int System.Collections.Generic.List<T>.FindIndex(System.Predicate<T> match)", hook30);
        //hook31
        InjectHook("TResult DotNetCommon.Extensions.ObjectExtensions.ModifyByDto<TResult>(TResult baseObj, System.Object dto)", hook31);
        //hook32
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetUpdateFluent<T>(System.Collections.Generic.List<T> list, int index, System.Func<T, T> func)", hook32);
        InjectHook("T[] DotNetCommon.Extensions.ArrayExtensions.SetUpdateFluent<T>(T[] arr, int index, System.Func<T, T> func)", hook32);
        InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetUpdateFluent<T>(System.Collections.Generic.IList<T> list, int index, System.Func<T, T> func)", hook32);
        //hook33
        InjectHook("System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetUpdateFluent<T>(System.Collections.Generic.List<T> list, System.Func<T, bool> filter, System.Func<T, T> func)", hook33);
        InjectHook("T[] DotNetCommon.Extensions.ArrayExtensions.SetUpdateFluent<T>(T[] arr, System.Func<T, bool> filter, System.Func<T, T> func)", hook33);
        InjectHook("System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetUpdateFluent<T>(System.Collections.Generic.IList<T> list, System.Func<T, bool> filter, System.Func<T, T> func)", hook33);
        //hook34
        InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetUpdateFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)", hook34);
        //hook35
        InjectHook("int DotNetCommon.Extensions.JsonExtensions.FindIndex(System.Text.Json.Nodes.JsonArray jsonArray, System.Predicate<System.Text.Json.Nodes.JsonNode> match)", hook35);
        InjectHook("int DotNetCommon.Extensions.ArrayExtensions.FindIndex<T>(T[] arr, System.Predicate<T> match)", hook35);
        InjectHook("int DotNetCommon.Extensions.ListExtensions.FindIndex<T>(System.Collections.Generic.IList<T> list, System.Predicate<T> match)", hook35);
        //hook36
        InjectHook("System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.SetUpdateFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)", hook36);
        //hook37
        InjectHook("System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetUpdateFluent(System.Text.Json.Nodes.JsonArray jsonArray, System.Func<System.Text.Json.Nodes.JsonNode, bool> filter, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)", hook37);
        //hook38
        InjectHook("int System.Collections.Generic.List<T>.FindLastIndex(System.Predicate<T> match)", hook38);
        //hook39
        InjectHook("int DotNetCommon.Extensions.JsonExtensions.FindLastIndex(System.Text.Json.Nodes.JsonArray jsonArray, System.Predicate<System.Text.Json.Nodes.JsonNode> match)", hook39);
        InjectHook("int DotNetCommon.Extensions.ArrayExtensions.FindLastIndex<T>(T[] arr, System.Predicate<T> match)", hook39);
        InjectHook("int DotNetCommon.Extensions.ListExtensions.FindLastIndex<T>(System.Collections.Generic.IList<T> list, System.Predicate<T> match)", hook39);
        //hook40
        InjectHook("TSource System.Linq.Enumerable.ElementAt<TSource>(System.Collections.Generic.IEnumerable<TSource> source, int index)", hook40);
        //hook41
        InjectHook("System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetUpdateFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key, System.Func<TValue, TValue> setValFunc)", hook41);
        InjectHook("System.Collections.Generic.IDictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetUpdateFluent<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> dic, TKey key, System.Func<TValue, TValue> setValFunc)", hook41);
        //hook42
        InjectHook("bool DotNetCommon.Extensions.EnumerableExtensions.IsNotNullOrEmpty<T>(System.Collections.Generic.IEnumerable<T> sequence)", hook42);
        //hook43
        InjectHook("int System.Linq.Enumerable.Count<TSource>(System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate)", hook43);
    }
    private string json_contains(string doc1, string doc2)
    {
        return $"json_contains({doc1},{doc2})";
    }
    private object hook1(object arg)
    {
        //"bool System.Collections.Generic.List<T>.Contains(T item)"
        //"bool System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource> source, TSource value)"
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var method = ctx.Reflect;

        var isOne = method.Name.Contains("List<T>");
        var type = method.GenericTypes.First().type;
        if (type.IsNullable()) type = Nullable.GetUnderlyingType(type);
        (bool isEnumString, Type enumType) res = (false, null);
        if (type.IsEnum) res = ctx.IsEnumString(isOne ? call.Arguments[0] : call.Arguments[1]);

        if (res.isEnumString)
        {
            //非json, json中不允许这么定制
            var str1 = ctx.Visit(isOne ? call.Object : call.Arguments[0]);
            var str2 = ctx.Visit(isOne ? call.Arguments[0] : call.Arguments[1]);
            return $"{str2} in {str1}";
        }
        string op1 = string.Empty, op2 = string.Empty;
        op1 = ctx.Visit(isOne ? call.Object : call.Arguments[0]);
        if (op1.StartsWith('('))
        {
            //非json 如: (1,2,3)
            var arg2 = ctx.Visit(isOne ? call.Arguments[0] : call.Arguments[1]);
            var sql = $"{ctx.WrapBracket(arg2)} in {op1}";
            return sql;
        }
        //处理json
        var arr = ctx.GetJsonSqlSeg(isOne ? [call.Object, call.Arguments[0]] : [call.Arguments[0], call.Arguments[1]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Doc]);
        return json_contains(arr[0], arr[1]);
    }
    private object hook2(object arg)
    {
        //"bool System.Text.Json.Nodes.JsonArray.Contains(System.Text.Json.Nodes.JsonNode item)"
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var arr = ctx.GetJsonSqlSeg([call.Object, call.Arguments[0]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Doc]);
        return json_contains(arr[0], arr[1]);
    }
    private object hook3(object arg)
    {
        //bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAll<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values, System.Collections.Generic.IEqualityComparer<T> equalityComparer)
        //bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAll<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values)
        //bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAll<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects, System.Collections.Generic.IEqualityComparer<T> equalityComparer)
        //bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAll<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects)
        //bool DotNetCommon.Extensions.JsonExtensions.ContainsObject<T>(System.Text.Json.Nodes.JsonArray jsonArray, T value, System.Collections.Generic.IEqualityComparer<T> equalityComparer)
        //bool DotNetCommon.Extensions.JsonExtensions.ContainsObject<T>(System.Text.Json.Nodes.JsonArray jsonArray, T value)
        //bool System.Linq.Enumerable.Contains<TSource>(System.Collections.Generic.IEnumerable<TSource> source, TSource value, System.Collections.Generic.IEqualityComparer<TSource> comparer)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[1]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Doc]);
        return json_contains(arr[0], arr[1]);
    }
    private string memberAccess(string doc1, string jsonPathWrap, bool isConstantMember, Type returnType)
    {
        var returning = GetReturn(returnType);
        //json_value 要求path必须是常量,如: select json_value('[1,2]',concat('$[','1',']')) 会报错
        //可以使用如下代替: select cast(json_unquote(json_extract('[1,2]',concat('$[','1',']'))) as signed)
        if (isConstantMember)
        {
            //常量
            if (returning.IsNullOrWhiteSpace()) return $"json_value({doc1},{jsonPathWrap})";
            if (returning.Contains("datetime(6)"))
            {
                //使用convert代替,否则查询返回空,反序列化报错
                //convert(json_value(properties,'$."time"'),datetime(6))
                return $"convert(json_value({doc1},{jsonPathWrap}),datetime(6))";
            }
            else
            {
                //json_value(properties,'$.age' returning signed)
                return $"json_value({doc1},{jsonPathWrap} {returning})";
            }
        }
        else
        {
            //变量
            if (returning.IsNullOrWhiteSpace()) return $"json_unquote(json_extract({doc1},{jsonPathWrap}))";
            if (returning.Contains("datetime(6)"))
            {
                //使用convert代替,否则查询返回空,反序列化报错
                //convert(json_unquote(json_extract(properties,'$."time"')),datetime(6))
                return $"convert(json_unquote(json_extract({doc1},{jsonPathWrap})),datetime(6))";
            }
            else
            {
                //cast(json_unquote(json_extract(properties,'$.age')) as signed)
                return $"cast(json_unquote(json_extract({doc1},{jsonPathWrap}) as {returning.Substring("returning ".Length)})";
            }
        }

    }
    private object hook4(object arg)
    {
        //TValue System.Collections.Generic.Dictionary<TKey, TValue>.get_Item(TKey key)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var doc = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
        var ret = ctx.GetConstant(call.Arguments[0], ctx.MidValues, null);
        var member = string.Empty;
        if (ret.Success)
        {
            member = ctx.db.GetJsonPathSegByPropertyName(ret.Data.ToString());
            return memberAccess(doc, member, true, call.Type);
        }
        member = ctx.Visit(call.Arguments[0]);
        member = $"concat('$.',{member})";
        return memberAccess(doc, member, false, call.Type);
    }
    private object hook5(object arg)
    {
        //T System.Collections.Generic.List<T>.get_Item(int index)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var doc = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
        var ret = ctx.GetConstant(call.Arguments[0], ctx.MidValues, null);
        var member = string.Empty;
        if (ret.Success)
        {
            member = $"'$[{ret.Data.To<int>()}]'";
            return memberAccess(doc, member, true, call.Type);
        }
        member = ctx.Visit(call.Arguments[0]);
        member = $"concat('$[',{member},']')";
        return memberAccess(doc, member, false, call.Type);
    }
    private object hook6(object arg)
    {
        //System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(string propertyName)
        return hook4(arg);
    }
    private object hook7(object arg)
    {
        //System.Text.Json.Nodes.JsonNode System.Text.Json.Nodes.JsonNode.get_Item(int index)
        return hook5(arg);
    }
    private object hook8(object arg)
    {
        //string System.Object.ToString()
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.Visit(call.Object);
        return ctx.db.ConvertSqlSegment.ConvertAllToString(op1);
    }
    private object hook9(object arg)
    {
        //T System.Text.Json.Nodes.JsonNode.GetValue<T>()
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.Visit(call.Object);
        return setJsonReturn(op1, call.Type);
    }
    private string json_contains_path(string doc1, string jsonPathWrap)
    {
        return $"json_contains_path({doc1},'one',{jsonPathWrap})";
    }
    private object hook10(object arg)
    {
        //bool System.Text.Json.Nodes.JsonObject.ContainsKey(string propertyName)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var db = ctx.db;

        var doc = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
        var ret = ctx.GetConstant(call.Arguments[0], ctx.MidValues, null);
        var member = string.Empty;
        if (ret.Success)
        {
            member = ctx.db.GetJsonPathSegByPropertyName(ret.Data.ToString());
            return json_contains_path(doc, member);
        }
        else
        {
            member = ctx.Visit(call.Arguments[0]);
            return json_contains_path(doc, $"concat('$.',{member})");
        }
    }
    private object hook11(object arg)
    {
        //bool System.Collections.Generic.Dictionary<TKey, TValue>.ContainsKey(TKey key)
        return hook10(arg);
    }
    private string json_overlaps(string doc1, string doc2)
    {
        return $"json_overlaps({doc1},{doc2})";
    }
    private object hook12(object arg)
    {
        //bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAny<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects, System.Collections.Generic.IEqualityComparer<T> equalityComparer)
        //bool DotNetCommon.Extensions.JsonExtensions.ContainsObjectAny<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> objects)
        //bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAny<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values, System.Collections.Generic.IEqualityComparer<T> equalityComparer)
        //bool DotNetCommon.Extensions.EnumerableExtensions.ContainsAny<T>(System.Collections.Generic.IEnumerable<T> sequence, System.Collections.Generic.IEnumerable<T> values)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[1]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Doc]);
        return json_overlaps(arr[0], arr[1]);
    }
    private object hook13(object arg)
    {
        //T DotNetCommon.Extensions.StringExtensions.ToObject<T>(string input)
        //System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.ObjectExtensions.ToJsonObject(System.Object obj)
        //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.ObjectExtensions.ToJsonArray(System.Object obj)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.Visit(call.Arguments[0]);
        return getJsonFormatFromSqlSeg(op1);
    }
    private object hook14(object arg)
    {
        //int System.Linq.Enumerable.Count<TSource>(System.Collections.Generic.IEnumerable<TSource> source)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.Visit(call.Arguments[0]);
        return $"json_length({op1})";
    }
    private object hook15(object arg)
    {
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddFluent<T>(System.Collections.Generic.List<T> list, T item)
        //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddFluent<T>(System.Collections.Generic.IList<T> list, T item)
        //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.AddFluent(System.Text.Json.Nodes.JsonArray jsonArray, System.Text.Json.Nodes.JsonNode item)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[1]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Value]);
        return getJsonArrayAppend(arr[0], arr[1]);
    }
    private object hook16(object arg)
    {
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items)
        //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.AddRangeFluent<T>(System.Text.Json.Nodes.JsonArray jsonArray, System.Collections.Generic.IEnumerable<T> items)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[1]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Doc]);
        return getJsonArrayMergePreserve(arr[0], arr[1]);
    }
    private object hook17(object arg)
    {
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveAtFluent<T>(System.Collections.Generic.List<T> list, int index)
        //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.RemoveAtFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        var _val = ret.Success ? ret.Data : ctx.Visit(call.Arguments[1]);
        return getJsonArrayRemove(doc, _val, ret.Success);
    }
    private object hook18(object arg)
    {
        //System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.RemoveFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key)
        //System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.RemoveFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var method = ctx.Reflect;

        var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        var member = string.Empty;
        if (ret.Success)
        {
            member = ctx.db.GetJsonPathSegByPropertyName(ret.Data.ToString());
        }
        else
        {
            member = ctx.Visit(call.Arguments[1]);
            member = $"concat('$.',{member})";
        }
        return getJsonObjectRemove(doc, member);
    }
    private object hook19(object arg)
    {
        return $"json_array()";
    }
    private object hook20(object arg)
    {
        return $"json_object()";
    }
    private string json_set(string doc, string jsonPathWrap, string val)
    {
        return $"json_set({doc},{jsonPathWrap},{val})";
    }
    private string json_array_insert(string doc, string jsonPathWrap, string val)
    {
        return $"json_array_insert({doc},{jsonPathWrap},{val})";
    }
    private object hook21(object arg)
    {
        //System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key, System.Text.Json.Nodes.JsonNode value)
        //System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key, TValue value)
        //System.Collections.Generic.IDictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetFluent<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> dic, TKey key, TValue value)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var method = ctx.Reflect;

        var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[2]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Value]);
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        var member = string.Empty;
        if (ret.Success)
        {
            member = ctx.db.GetJsonPathSegByPropertyName(ret.Data.ToString());
        }
        else
        {
            member = ctx.Visit(call.Arguments[1]);
            member = $"concat('$.',{member})";
        }
        var doc = arr[0];
        return json_set(doc, member, arr[1]);
    }
    private object hook22(object arg)
    {
        //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Text.Json.Nodes.JsonNode b)
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.List<T> list, int index, T b)
        //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetFluent<T>(System.Collections.Generic.IList<T> list, int index, T b)
        //T[] DotNetCommon.Extensions.ArrayExtensions.SetFluent<T>(T[] arr, int index, T b)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var method = ctx.Reflect;

        var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[2]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Value]);
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        var member = string.Empty;
        var path = string.Empty;
        if (ret.Success)
        {
            member = ret.Data.ToString();
            path = $"'$[{member}]'";
        }
        else
        {
            member = ctx.Visit(call.Arguments[1]);
            path = $"concat('$[',{member},']')";
        }
        var doc = arr[0];
        return json_set(doc, path, arr[1]);
    }
    private object hook23(object arg)
    {
        //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.InsertAtFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Text.Json.Nodes.JsonNode item)
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.InsertAtFluent<T>(System.Collections.Generic.List<T> list, int index, T b)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var method = ctx.Reflect;

        var arr = ctx.GetJsonSqlSeg([call.Arguments[0], call.Arguments[2]], [EnumJsonAcceptAsType.Doc, EnumJsonAcceptAsType.Value]);
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        var member = string.Empty;
        var path = string.Empty;
        if (ret.Success)
        {
            member = ret.Data.ToString();
            path = $"'$[{member}]'";
        }
        else
        {
            member = ctx.Visit(call.Arguments[1]);
            path = $"concat('$[',{member},']')";
        }
        var doc = arr[0];
        return json_array_insert(doc, path, arr[1]);
    }
    private object hook24(object arg)
    {
        //bool System.Linq.Enumerable.Any<TSource>(System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate)
        //bool System.Linq.Enumerable.All<TSource>(System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate)
        //bool System.Linq.Enumerable.Any<TSource>(System.Collections.Generic.IEnumerable<TSource> source)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var method = ctx.Reflect;

        var isAll = method.Name.Contains("All<TSource>");
        var isAnyNoLambda = false;
        if (!isAll) isAnyNoLambda = !method.Name.Contains("predicate");
        var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        if (isAnyNoLambda)
        {
            return $"json_length({op1})>0";
        }
        var lambda = call.Arguments[1] as LambdaExpression;
        var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
        if (isAll)
        {
            return $"""
                    json_length({op1})=(select count(1) from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t where {op2})
                    """;
        }
        return $"""
                0<(select 1 from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t where {op2} limit 1)
                """;
    }
    private object hook25(object arg)
    {
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddIfNotExistFluent<T>(System.Collections.Generic.List<T> list, T item)
        //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, T item)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var op2Doc = string.Empty;
        var op2Val = string.Empty;
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        if (ret.Success)
        {
            op2Doc = ctx.db.ConvertJsonLiteralToSql(ret.Data, EnumJsonAcceptAsType.Doc).res;
            op2Val = ctx.db.ConvertJsonLiteralToSql(ret.Data, EnumJsonAcceptAsType.Value).res;
        }
        else op2Doc = op2Val = ctx.Visit(call.Arguments[1]);
        return $"case when json_contains({op1},{op2Doc}) then {op1} else json_merge_preserve({op1},json_array({op2Val})) end";
    }
    private object hook26(object arg)
    {
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items, bool isDistinct)
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.List<T> list, System.Collections.Generic.IEnumerable<T> items)
        //System.Collections.Generic.IList<T DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, System.Collections.Generic.IEnumerable<T> items, bool isDistinct)
        //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.AddRangeIfNotExistFluent<T>(System.Collections.Generic.IList<T> list, System.Collections.Generic.IEnumerable<T> items)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var method = ctx.Reflect;

        var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var isDistinct = false;
        if (method.Name.Contains("isDistinct"))
        {
            var ret3 = ctx.GetConstant(call.Arguments[2], ctx.MidValues, null);
            if (!ret3.Success) throw new Exception($"方法 list.AddRangeIfNotExistFluent(arr,isDistinct) 中的 isDistinct 必须是常量!");
            isDistinct = (bool)ret3.Data;
        }
        var op2 = string.Empty;
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        if (ret.Success)
        {
            op2 = ctx.db.ConvertJsonLiteralToSql(ret.Data, EnumJsonAcceptAsType.Doc).res;
        }
        else op2 = ctx.Visit(call.Arguments[1]);
        if (isDistinct)
        {
            return $"""
                    (select json_arrayagg(i) from(  
                        select i from json_table({op1},'$[*]' columns(i json path '$')) t
                        union
                        select i from json_table({op2},'$[*]' columns(i json path '$')) t
                    ) t)
                    """;
        }
        else
        {
            return $"""
                    (select json_arrayagg(i) from(  
                        select i from json_table({op1},'$[*]' columns(i json path '$')) t
                        union all
                        select distinct i from json_table({op2},'$[*]' columns(i json path '$')) t where t.i not in (select i from json_table({op1},'$[*]' columns(i json path '$')) t)
                    ) t)
                    """;
        }
    }
    private object hook27(object arg)
    {
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveFluent<T>(System.Collections.Generic.List<T> list, T b)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var method = ctx.Reflect;

        var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var op2 = string.Empty;
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        if (ret.Success)
        {
            op2 = ctx.db.ConvertJsonLiteralToSql(ret.Data, EnumJsonAcceptAsType.Value).res;
        }
        else op2 = ctx.Visit(call.Arguments[1]);
        return $"(select json_arrayagg(t.i) from json_table({op1},'$[*]' columns(i json path '$')) t where t.i<>{op2})";
    }
    private object hook28(object arg)
    {
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.RemoveFluent<T>(System.Collections.Generic.List<T> list, System.Predicate<T> match)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var method = ctx.Reflect;

        var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var lambda = call.Arguments[1] as LambdaExpression;
        var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);

        var name = ctx.db.AddQuote("a" + lambda.Parameters.First().Name);
        return $"""
                (select json_arrayagg(t.{name}) from json_table({op1},'$[*]' columns({name} json path '$',{ctx.JsonTableParaToColumns(jsonTablePara)}))t where not ({op2}))
                """;
    }
    private object hook29(object arg)
    {
        //int System.Collections.Generic.List<T>.IndexOf(T item)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;
        var method = ctx.Reflect;

        var op1 = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
        var op2 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Value])[0];
        return $"(select t.idx-1 from(select row_number() over() as idx,t.i from json_table({op1}, '$[*]' columns (i json path '$')) t) t where t.i={op2} limit 1)";
    }
    private object hook30(object arg)
    {
        //int System.Collections.Generic.List<T>.FindIndex(System.Predicate<T> match)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
        var lambda = call.Arguments[0] as LambdaExpression;
        var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
        return $"(select idx-1 from (select row_number() over() as idx,t.* from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t) t where {op2} limit 1)";
    }
    private object hook31(object arg)
    {
        //TResult DotNetCommon.Extensions.ObjectExtensions.ModifyByDto<TResult>(TResult baseObj, System.Object dto)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var op2 = ctx.GetJsonSqlSeg([call.Arguments[1]], [EnumJsonAcceptAsType.Doc])[0];
        return $"json_merge_patch({op1},{op2})";
    }
    private object hook32(object arg)
    {
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetUpdateFluent<T>(System.Collections.Generic.List<T> list, int index, System.Func<T, T> func)
        //T[] DotNetCommon.Extensions.ArrayExtensions.SetUpdateFluent<T>(T[] arr, int index, System.Func<T, T> func)
        //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetUpdateFluent<T>(System.Collections.Generic.IList<T> list, int index, System.Func<T, T> func)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        var member = string.Empty;
        var path = string.Empty;
        if (ret.Success)
        {
            member = ret.Data.ToString();
            path = $"'$[{member}]'";
        }
        else
        {
            member = ctx.Visit(call.Arguments[1]);
            path = $"concat('$[',{member},']')";
        }

        var lambda = call.Arguments[2] as LambdaExpression;
        var para = lambda.Parameters.First();
        var paraAlias = memberAccess(doc, path, ret.Success, para.Type);
        ctx.AddParameter(new KeyValuePair<ParameterExpression, string>(para, paraAlias), true);
        var val = ctx.Visit(lambda);
        ctx.RemoveParameter(para);
        return json_set(doc, path, val);
    }

    private object hook33(object arg)
    {
        //System.Collections.Generic.List<T> DotNetCommon.Extensions.ListExtensions.SetUpdateFluent<T>(System.Collections.Generic.List<T> list, System.Func<T, bool> filter, System.Func<T, T> func)
        //T[] DotNetCommon.Extensions.ArrayExtensions.SetUpdateFluent<T>(T[] arr, System.Func<T, bool> filter, System.Func<T, T> func)
        //System.Collections.Generic.IList<T> DotNetCommon.Extensions.ListExtensions.SetUpdateFluent<T>(System.Collections.Generic.IList<T> list, System.Func<T, bool> filter, System.Func<T, T> func)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var lambda1 = call.Arguments[1] as LambdaExpression;
        var p = lambda1.Parameters.First();
        var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda1);

        var lambda2 = call.Arguments[2] as LambdaExpression;
        var _para = lambda2.Parameters.First();
        ctx.AddParameter(new KeyValuePair<ParameterExpression, string>(_para, p.Name), true);
        var (jsonTablePara2, op3) = ctx.JsonTableSpread(lambda2);
        ctx.RemoveParameter(_para);
        jsonTablePara.AddRange(jsonTablePara2);
        jsonTablePara.Add((p.Name, null, "json"));
        return $"""
                (select json_arrayagg(case when {op2} then {op3} else t.`{p.Name}` end) from json_table({doc},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t)
                """;
    }

    private object hook34(object arg)
    {
        //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetFluent(System.Text.Json.Nodes.JsonArray jsonArray, int index, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)
        return hook32(arg);
    }
    private object hook35(object arg)
    {
        //int DotNetCommon.Extensions.JsonExtensions.FindIndex(System.Text.Json.Nodes.JsonArray jsonArray, System.Predicate<System.Text.Json.Nodes.JsonNode> match)
        //int DotNetCommon.Extensions.ArrayExtensions.FindIndex<T>(T[] arr, System.Predicate<T> match)
        //int DotNetCommon.Extensions.ListExtensions.FindIndex<T>(System.Collections.Generic.IList<T> list, System.Predicate<T> match)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var lambda = call.Arguments[1] as LambdaExpression;
        var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
        return $"(select idx-1 from (select row_number() over() as idx,t.* from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t) t where {op2} limit 1)";
    }
    private object hook36(object arg)
    {
        //System.Text.Json.Nodes.JsonObject DotNetCommon.Extensions.JsonExtensions.SetUpdateFluent(System.Text.Json.Nodes.JsonObject jsonObject, string key, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        var member = string.Empty;
        if (ret.Success)
        {
            member = ctx.db.GetJsonPathSegByPropertyName(ret.Data.ToString());
        }
        else
        {
            member = ctx.Visit(call.Arguments[1]);
            member = $"concat('$.',{member})";
        }

        var lambda = call.Arguments[2] as LambdaExpression;
        var para = lambda.Parameters.First();
        var paraAlias = memberAccess(doc, member, ret.Success, para.Type);
        ctx.AddParameter(new KeyValuePair<ParameterExpression, string>(para, paraAlias), true);
        var val = ctx.Visit(lambda);
        ctx.RemoveParameter(para);
        return json_set(doc, member, val);
    }

    private object hook37(object arg)
    {
        //System.Text.Json.Nodes.JsonArray DotNetCommon.Extensions.JsonExtensions.SetUpdateFluent(System.Text.Json.Nodes.JsonArray jsonArray, System.Func<System.Text.Json.Nodes.JsonNode, bool> filter, System.Func<System.Text.Json.Nodes.JsonNode, System.Text.Json.Nodes.JsonNode> func)
        return hook33(arg);
    }
    private object hook38(object arg)
    {
        //int System.Collections.Generic.List<T>.FindLastIndex(System.Predicate<T> match)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.GetJsonSqlSeg([call.Object], [EnumJsonAcceptAsType.Doc])[0];
        var lambda = call.Arguments[0] as LambdaExpression;
        var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
        return $"(select idx-1 as idx from (select row_number() over() as idx,t.* from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t) t where {op2} order by t.idx desc limit 1)";
    }
    private object hook39(object arg)
    {
        //int DotNetCommon.Extensions.JsonExtensions.FindLastIndex(System.Text.Json.Nodes.JsonArray jsonArray, System.Predicate<System.Text.Json.Nodes.JsonNode> match)
        //int DotNetCommon.Extensions.ArrayExtensions.FindLastIndex<T>(T[] arr, System.Predicate<T> match)
        //int DotNetCommon.Extensions.ListExtensions.FindLastIndex<T>(System.Collections.Generic.IList<T> list, System.Predicate<T> match)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var lambda = call.Arguments[1] as LambdaExpression;
        var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
        return $"(select idx-1 as idx from (select row_number() over() as idx,t.* from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t) t where {op2} order by t.idx desc limit 1)";
    }
    private object hook40(object arg)
    {
        //TSource System.Linq.Enumerable.ElementAt<TSource>(System.Collections.Generic.IEnumerable<TSource> source, int index)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        string member;
        if (ret.Success)
        {
            //常量
            member = $"'$[{ret.Data.To<int>()}]'";
        }
        else
        {
            //变量
            var op2 = ctx.Visit(call.Arguments[1]);
            member = $"concat('$[',{op2},']')";
        }
        return memberAccess(op1, member, ret.Success, call.Type);
    }
    private object hook41(object arg)
    {
        //System.Collections.Generic.Dictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetUpdateFluent<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> dic, TKey key, System.Func<TValue, TValue> func)
        //System.Collections.Generic.IDictionary<TKey, TValue> DotNetCommon.Extensions.DictionaryExtensions.SetUpdateFluent<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> dic, TKey key, System.Func<TValue, TValue> func)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var ret = ctx.GetConstant(call.Arguments[1], ctx.MidValues, null);
        var member = string.Empty;
        if (ret.Success)
        {
            member = ctx.db.GetJsonPathSegByPropertyName(ret.Data.ToString());
        }
        else
        {
            member = ctx.Visit(call.Arguments[1]);
            member = $"concat('$.',{member})";
        }

        var lambda = call.Arguments[2] as LambdaExpression;
        var para = lambda.Parameters.First();
        var paraAlias = memberAccess(doc, member, ret.Success, para.Type);
        ctx.AddParameter(new KeyValuePair<ParameterExpression, string>(para, paraAlias), true);
        var val = ctx.Visit(lambda);
        ctx.RemoveParameter(para);
        return json_set(doc, member, val);
    }

    private object hook42(object arg)
    {
        //bool DotNetCommon.Extensions.EnumerableExtensions.IsNotNullOrEmpty<T>(System.Collections.Generic.IEnumerable<T> sequence)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var doc = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        return $"json_length({doc})>0";
    }

    private object hook43(object arg)
    {
        //int System.Linq.Enumerable.Count<TSource>(System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate)
        var ctx = arg as MethodHookContext;
        var call = ctx.Expression as MethodCallExpression;

        var op1 = ctx.GetJsonSqlSeg([call.Arguments[0]], [EnumJsonAcceptAsType.Doc])[0];
        var lambda = call.Arguments[1] as LambdaExpression;
        var (jsonTablePara, op2) = ctx.JsonTableSpread(lambda);
        return $"""
                (select count(1) from json_table({op1},'$[*]' columns({ctx.JsonTableParaToColumns(jsonTablePara)})) t where {op2})
                """;
    }

    private string setJsonReturn(string op1, Type returnType)
    {
        var jsonExp = op1;
        if (jsonExp.IsNullOrWhiteSpace()) return jsonExp;
        var returning = GetReturn(returnType);
        if (!jsonExp.StartsWith("json_value("))
        {
            if (returning.IsNullOrWhiteSpace()) return jsonExp;
            if (returning.Contains("datetime(6)")) return $"convert(json_value({jsonExp},'$'),datetime(6))";
            return $"json_value({jsonExp},'$' {returning})";
        }
        if (jsonExp.EndsWith("')"))
        {
            if (returning.IsNullOrWhiteSpace()) return jsonExp;
            if (returning.Contains("datetime(6)"))
            {
                //使用convert代替,否则查询返回空,反序列化报错
                //json_value(properties,'$.ext.time') => convert(json_value(properties,'$.ext.time'),datetime(6))
                return $"convert({jsonExp},datetime(6))";
            }
            //json_value(t.properties,'$.ext.age') => json_value(t.properties,'$.ext.age' returning signed)
            return jsonExp.Substring(0, jsonExp.Length - 1) + " " + returning + ")";
        }
        return jsonExp;
    }

    private string GetReturn(Type type)
    {
        if (type == null) return "";
        var typeCode = type.IsNullable() ? Nullable.GetUnderlyingType(type).GetTypeCode() : type.GetTypeCode();
        var returning = string.Empty;
        switch (typeCode)
        {
            case TypeCode.Boolean:
            case TypeCode.Byte:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
                returning = "returning signed";
                break;
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
                returning = "returning signed";
                break;
            case TypeCode.Single:
                if (this.IsMySql8Compatible())
                {
                    returning = "returning float";
                }
                else
                {
                    returning = "returning decimal(30,15)";
                }
                break;
            case TypeCode.Double:
                if (this.IsMySql8Compatible())
                {
                    returning = "returning double";
                }
                else
                {
                    returning = "returning decimal(30,15)";
                }
                break;
            case TypeCode.Decimal:
                returning = "returning decimal(30,15)";
                break;
            case TypeCode.String:
            case TypeCode.Char:
                returning = "returning char";
                break;
            case TypeCode.DateTime:
                returning = "returning datetime(6)";
                break;
            default:
                break;
        }
        if (returning == string.Empty)
        {
            var typeClassFullName = type.GetClassFullName();
            switch (typeClassFullName)
            {
                case "string":
                    returning = "returning char";
                    break;
                case "System.DateTime":
                case "System.DateTime?":
                    returning = "returning decimal(30,15)";
                    break;
                case "System.DateOnly":
                case "System.DateOnly?":
                    returning = "returning date";
                    break;
                case "System.TimeSpan":
                case "System.TimeSpan?":
                case "System.TimeOnly":
                case "System.TimeOnly?":
                    returning = "returning time(6)";
                    break;
                default:
                    break;
            }
        }
        return returning;
    }
    private string getJsonFormatFromSqlSeg(string op1)
    {
        //new{}.ToJsonObject
        //new{}.ToJsonArray
        //str.ToObject<T>

        //select cast('null' as json)
        //select cast(json_value('[1]','$[0]') as json)
        var jsonExp = op1;
        if (jsonExp.StartsWith("json_object") || jsonExp.StartsWith("json_array")) return jsonExp;
        if (jsonExp == null) return "cast('null' as json)";
        return $"cast({jsonExp} as json)";
    }
    private object getJsonArrayAppend(string op1, string op2)
    {
        return $"json_array_append({op1},'$',{op2})";
    }
    private object getJsonArrayMergePreserve(string op1, string op2)
    {
        return $"json_merge_preserve({op1},{op2})";
    }
    private string getJsonArrayRemove(string op1, object op2, bool isConstant)
    {
        if (isConstant)
        {
            //常量
            var idx = (int)op2;
            return $"json_remove({op1},'$[{idx}]')";
        }
        //非常量
        var str = (string)op2;
        //为什么这么写?
        //select json_remove('[1,2,3]',concat('$[',null,']')) //输出null, 这个破坏力太强
        //select json_remove('[1,2,3]','$.null') //仍然输出 [1,2,3]
        return $"json_remove({op1},ifnull(concat('$[',{str},']'),'$.null'))";
    }

    private string getJsonObjectRemove(string op1, string jsonPathWrap)
    {
        return $"json_remove({op1},{jsonPathWrap})";
    }
}